import logging
import threading
from asyncrunner import AsyncRunner
from plswriter import WriterFactory
from suckpls import SuckPls

class ProgressPrinter:
    def __init__(self, async_runner, logger):
        self._async_runner = async_runner
        self._log = logger
        self._async_runner._callback_start = self.print_start
        self._async_runner._callback_done = self.print_done
        self._lock = threading.Lock()

    def print_start(self, call):
        with self._lock:
            self._log.info('sucking: %s' % call[3]['seed'])

    def print_done(self, call):
        with self._lock:
            stats = self._async_runner.stats()
            # Add 1 to count myself as completed.
            self._log.info('%i/%i done: %s' % (stats[2] + 1,
                                               sum(stats), call[3]['seed']))


# TODO need a lock for logging?
class ActiveCallsPrinter:
    def __init__(self, async_runner, logger, interval = 5):
        self._async_runner = async_runner
        self._log = logger
        self._interval = interval
        self._lock = threading.Lock()
        self._active = True

    def start(self):
        with self._lock:
            if self._active:
                self._timer = self._make_timer()
                self._timer.start()

    def cancel(self):
        with self._lock:
            self._timer.cancel()
            self._active = False

    def _make_timer(self):
        return threading.Timer(self._interval,
                               self._print_active_and_restart)

    def _print_active_and_restart(self):
        self._log.info('active: %s' % self._async_runner.get_active_calls())
        self.start()


class SuckRunner:
    def run(self, suck_calls):
        '''
        suck_calls is a list of SuckPls invocation kwargs (see SuckPls.suck()).
        The 'writers' value is passed to WriterFactory.get_writers() and
        replaced with the result.
        '''

        # Setup logging.
        logger = logging.getLogger()  # root logger
        logger.setLevel(logging.INFO)
        handler = logging.StreamHandler()
        handler.setFormatter(logging.Formatter(
            '%(asctime)s - %(levelname)-8s - %(name)s - %(message)s'))
        logger.addHandler(handler)

        # Factory is shared across all invocations.
        writer_factory = WriterFactory()

        # Configure workers.
        runner = AsyncRunner(len(suck_calls))  # TODO have an upper limit
        ProgressPrinter(runner, logger)  # sets runner's callbacks
        active_printer = ActiveCallsPrinter(runner, logger)
        for suck_args in suck_calls:
            suck = SuckPls(writer_factory)
            # TODO the netloc would make a better name than the seed
            runner.add_call(suck_args.get('name', suck_args['seed']),
                            suck.suck,
                            **suck_args)

        runner.start()
        active_printer.start()
        # TODO probably want a timeout
        runner.join()
        active_printer.cancel()

        writer_factory.flush_all()

        logger.info('---------- all done ----------')
